import React, { useEffect, useRef, useState } from "react";

type TextStyle = {
  bold?: boolean;
  italic?: boolean;
};

type ASTNode = {
  type: string;
  children?: ASTNode[];
  content?: string;
  styles?: TextStyle;
  data?: any;
};

function parseHTML(html: string) {
  const parser = new DOMParser();
  const doc = parser.parseFromString(html, "text/html");

  // 获取所有节点
  const node = doc.body.childNodes;

  function parseNode(node: Node): ASTNode | null {
    // 文本节点
    if (node.nodeType === Node.TEXT_NODE) {
      return {
        type: "text",
        content: node.textContent || "",
        styles: {},
      };
    }

    // 元素节点
    if (node.nodeType === Node.ELEMENT_NODE) {
      const element = node as Element;
      const tag = element.tagName.toLowerCase();

      if (tag === "p") {
        return {
          type: "paragraph",
          children: Array.from(element.childNodes)
            .map(parseNode)
            .filter(Boolean) as ASTNode[],
        };
      }

      if (["strong", "b"].includes(tag)) {
        return {
          type: "text",
          content: element.textContent || "",
          styles: { bold: true },
        };
      }

      if (["em", "i"].includes(tag)) {
        return {
          type: "text",
          content: element.textContent || "",
          styles: { italic: true },
        };
      }

      if (tag === "span" && element.hasAttribute("data-id")) {
        return {
          type: "mention",
          content: element.textContent || "",
          data: {
            id: element.getAttribute("data-id"),
          },
        };
      }

      return { type: "text", content: element.textContent || "", styles: {} };
    }

    return null;
  }

  return Array.from(node).map(parseNode).filter(Boolean) as ASTNode[];
}

function renderAST(ast: ASTNode[]): string { 
  return ast
    .map((node) => {
      if (node.type === "text") {
        let content = node.content || "";
        if (node.styles?.bold) {
          content = `<b>${content}</b>`;
        }
        if (node.styles?.italic) {
          content = `<i>${content}</i>`;
        }
        return content;
      }
      if (node.type === "paragraph") {
        return `<p>${renderAST(node.children || [])}</p>`;
      }
      if (node.type === "mention") {
        return `<span class="mention" data-id="${node.data?.id}">${node.content}</span>`;
      }
      return "";
    })
    .join("");
}

export function AdvancedEditor() {
  // 当前编辑的内容 AST
  const [ast, setAst] = useState<ASTNode[]>([]);
  // 编辑器的 DOM 引用
  const editorRef = useRef<HTMLDivElement>(null);
  // 撤销栈
  const [undoStack, setUndoStack] = useState<ASTNode[][]>([]);
  // 恢复栈
  const [redoStack, setRedoStack] = useState<ASTNode[][]>([]);

  // 是否处理中文输入法组合输入状态
  const isComposing = useRef(false);

  // 处理中文输入触发事件
  const handleCompositionStart = () => {
    isComposing.current = true;
  };

  const handleCompositionEnd = () => {
    isComposing.current = false;
    handleInput();
  };

  useEffect(() => {
    if (editorRef.current) {
      const html = editorRef.current.innerHTML;
      const parsedAST = parseHTML(html);
      setAst(parsedAST);
    }
  }, []);

  useEffect(() => {
    if (editorRef.current) {
      editorRef.current.innerHTML = renderAST(ast);
      const selection = window.getSelection();
      if (selection) {
        selection.removeAllRanges();
        const range = document.createRange();
        range.selectNodeContents(editorRef.current);
        range.collapse(false); // 将光标放在内容的末尾
        selection.addRange(range);
      }
    }
  }, [ast]);

  const handleInput = () => {
    if (isComposing.current) return;
    if (editorRef.current) {
      const html = editorRef.current.innerHTML;
      const parsedAST = parseHTML(html);
      // 将当前 AST 压入撤销栈
      setUndoStack((prev) => [...prev, ast]);
      setRedoStack([]); // 清空重做栈
      setAst(parsedAST);
    }
  };

  const applyFormat = (format: "bold" | "italic") => {
    const selection = window.getSelection();
    if (selection && selection.rangeCount > 0) {
      const range = selection.getRangeAt(0);
      const newNode = document.createElement(
        format === "bold" ? "strong" : "em"
      );
      const selectedNodes = getSelectedNodes(range);

      if (selectedNodes.length > 0) {
        selectedNodes.forEach((node) => {
          // 新建的Range,用来包裹节点被选中的部分
          const nodeRange = document.createRange();
          if (node === range.startContainer) {
            // 如果是选区的起始节点，从七点位置开始包裹到节点结尾
            nodeRange.setStart(node, range.startOffset);
            nodeRange.setEnd(node, (node as Text).length);
          }
          else if (node === range.endContainer) {
            // 如果是选区的结束节点，从节点开头开始包裹到选区结束位置
            nodeRange.setStart(node, 0);
            nodeRange.setEnd(node, range.endOffset);
          }
          else {
            nodeRange.selectNodeContents(node);
          }
          // 用克隆的格式包裹选中的内容
          nodeRange.surroundContents(newNode.cloneNode() as HTMLElement);

        })
      } else { 
        range.surroundContents(newNode);
      }
      handleInput();
    }
  };

  const getSelectedNodes = (range: Range) => {
    const nodes: Node[] = [];
    // 之前选取使用的range.extractContents();
    // 会修改源文档，他会把选区中的DOM从页面剪切出来
    // 返回的是一个DocumentFragment，他会包含所有节点
    // <p>hello <b>world</b></p>

    // 使用range.intersectsNode(node)来检查节点是否在选区内
    // 这个方法比较宽松，只要判断有重叠就会返回true
    // 比如Hello|World

    // 创建一个树遍历器
    const walker = document.createTreeWalker(
      // 选取内所有公共的祖先节点
      range.commonAncestorContainer,
      // 只遍历文本节点
      NodeFilter.SHOW_TEXT
    );

    // 在浏览器里面，一个选区Range有两个坐标表示
    // startContainer 和 startOffset
    // endContainer 和 endOffset
    // 对于一个文本节点来说
    // endOffset 是一个数字，表示 光标在这个文本的第几个字符后面
    // endOffset === node.length 就表示光标正好在文本节点的最后一个字符后面
    let node: Node | null;
    while ((node = walker.nextNode())) {
      if (
        range.intersectsNode(node) &&
        (node !== range.startContainer || range.startOffset > 0) &&
        (node !== range.endContainer || range.endOffset < (node as Text).length)
      ) {
        nodes.push(node);
      }
    }
    return nodes;
  };

  // 插入 @ 提及内容
  const insertMention = () => {
    const selection = window.getSelection();
    if (selection && selection.rangeCount > 0) {
      const range = selection.getRangeAt(0);
      const mentionElement = document.createElement("span");
      mentionElement.className = "mention";
      mentionElement.setAttribute("data-id", "User1");
      mentionElement.textContent = "@User1";
      range.insertNode(mentionElement);
      range.setStartAfter(mentionElement);
      handleInput();
    }
  };

  const undo = () => { 
    if (undoStack.length > 0) { 
      // 获取上一个状态
      const prevAST = undoStack[undoStack.length - 1];
      // 将当前 AST 压入重做栈
      setRedoStack((prev) => [...prev, ast]);
      // 设置AST为上一个版本
      setAst(prevAST);
      // 从undo栈中弹出上一个版本
      setUndoStack((prev) => prev.slice(0, -1));
    }
  };

  const redo = () => { 
    if (redoStack.length > 0) { 
      // 获取被撤销的状态
      const nextAST = redoStack[redoStack.length - 1];
      // 将当前 AST 压入撤销栈
      setUndoStack((prev) => [...prev, ast]);
      // 应用redo的AST
      setAst(nextAST);
      // 从redo栈中弹出顶部的版本
      setRedoStack((prev) => prev.slice(0, -1));
    }
  };

  const buttonStyle: React.CSSProperties = {
    padding: "0.5rem 1rem",
    border: "none",
    borderRadius: "4px",
    backgroundColor: "#007bff",
    color: "#fff",
    marginRight: "0.5rem",
    cursor: "pointer",
  };

  // 禁用状态的按钮样式
  const disabledButtonStyle: React.CSSProperties = {
    ...buttonStyle,
    backgroundColor: "#ccc",
    cursor: "not-allowed",
  };

  return (
    <div
      style={{
        display: "flex",
        padding: "1rem",
        gap: "1rem",
        width: "100%",
        boxSizing: "border-box",
      }}
    >
      {/* 左侧区域： 按钮和编辑器 */}
      <div style={{ flex: 1, display: "flex", flexDirection: "column" }}>
        <div style={{ marginBottom: "0.75rem" }}>
          <button style={buttonStyle} onClick={() => applyFormat("bold")}>
            加粗
          </button>
          <button style={buttonStyle} onClick={() => applyFormat("italic")}>
            斜体
          </button>
          <button
            style={{ ...buttonStyle, backgroundColor: "#28a745" }}
            onClick={insertMention}
          >
            提及
          </button>
          <button
            style={undoStack.length === 0 ? disabledButtonStyle : buttonStyle}
            onClick={undo}
            disabled={undoStack.length === 0}
          >
            撤销
          </button>
          <button
            style={redoStack.length === 0 ? disabledButtonStyle : buttonStyle}
            onClick={redo}
            disabled={redoStack.length === 0}
          >
            恢复
          </button>
        </div>

        <div
          ref={editorRef}
          contentEditable
          suppressContentEditableWarning
          onCompositionStart={handleCompositionStart}
          onCompositionEnd={handleCompositionEnd}
          onInput={handleInput}
          style={{
            border: "1px solid #ccc",
            borderRadius: "4px",
            padding: "1rem",
            minHeight: "200px",
            overflowY: "auto",
            flex: 1,
          }}
        />
      </div>

      {/* 右侧区域： AST 预览 */}
      <div style={{ flex: 1, borderLeft: "1px solid #ccc", padding: "1rem" }}>
        <h3>AST 预览</h3>
        <pre style={{ whiteSpace: "pre-wrap", textAlign: "left" }}>
          {JSON.stringify(ast, null, 2)}
        </pre>
      </div>
    </div>
  );
}
